import smtplib
import asyncio
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import aiosmtplib
from .token_store import TokenStore
from models import SessionLocal, EmailEntry

class QQMailer:
    def __init__(self, store: TokenStore):
        self.store = store

    def send_auth_link(self, auth_url: str) -> bool:
        """
        发送单个授权链接邮件，基于 QQ SMTP 授权码
        """
        code, sender_cfg, receiver_cfg = self.store.get_email_settings()
        sender = sender_cfg
        receiver = receiver_cfg
        # 若数据库有默认配置，以数据库为准
        try:
            with SessionLocal() as s:
                acc = s.query(EmailEntry).filter(EmailEntry.type == "sender", EmailEntry.is_default == 1).first()
                if acc and acc.email:
                    sender = acc.email
        except Exception:
            pass
        # 当配置未提供单收件人时，尝试从列表与数据库回退
        if not receiver:
            try:
                lst = self.store.get_email_receivers_list()
                if lst:
                    receiver = lst[0]
            except Exception:
                receiver = receiver
            if not receiver:
                try:
                    with SessionLocal() as s:
                        accs = s.query(EmailEntry).filter(EmailEntry.type == "cc").all()
                        for a in accs:
                            if a and a.email:
                                receiver = a.email
                                break
                except Exception:
                    pass
        if not code or not sender or not receiver:
            return False
        try:
            with smtplib.SMTP_SSL("smtp.qq.com", 465) as server:
                server.login(sender, code)
                msg = MIMEText(f"请点击以下链接完成授权：\n{auth_url}", "plain", "utf-8")
                msg["Subject"] = "授权重置"
                msg["From"] = sender
                msg["To"] = receiver
                refused = server.sendmail(sender, [receiver], msg.as_string())
                return receiver not in refused
        except Exception:
            return False

    def send_auth_link_multi(self, auth_url: str, recipients: list[str]) -> dict[str, bool]:
        """
        群发授权链接到多个收件人，返回每个邮箱是否发送成功
        """
        code, sender, _ = self.store.get_email_settings()
        results: dict[str, bool] = {}
        if not code or not sender or not recipients:
            return {r: False for r in (recipients or [])}
        try:
            with smtplib.SMTP_SSL("smtp.qq.com", 465) as server:
                server.login(sender, code)
                for r in recipients:
                    try:
                        msg = MIMEText(f"请点击以下链接完成授权：\n{auth_url}", "plain", "utf-8")
                        msg["Subject"] = "授权重置"
                        msg["From"] = sender
                        msg["To"] = r
                        refused = server.sendmail(sender, [r], msg.as_string())
                        results[r] = (r not in refused)
                    except Exception:
                        results[r] = False
        except Exception:
            results = {r: False for r in recipients}
        return results

    def send_auth_link_multi_details(self, auth_url: str, recipients: list[str]) -> list[dict]:
        """
        群发授权链接，并返回每个收件人的详细结果（是否成功、耗时与错误）
        """
        code, sender_cfg, _ = self.store.get_email_settings()
        sender = sender_cfg
        try:
            with SessionLocal() as s:
                acc = s.query(EmailEntry).filter(EmailEntry.type == "sender", EmailEntry.is_default == 1).first()
                if acc and acc.email:
                    sender = acc.email
        except Exception:
            pass
        if not code or not sender or not recipients:
            return [{"to": r, "ok": False, "latency_ms": 0, "error": "missing_config_or_recipients"} for r in (recipients or [])]
        results: list[dict] = []
        try:
            import time
            with smtplib.SMTP_SSL("smtp.qq.com", 465) as server:
                server.login(sender, code)
                for r in recipients:
                    start = time.perf_counter()
                    ok = False
                    err = None
                    try:
                        msg = MIMEText(f"请点击以下链接完成授权：\n{auth_url}", "plain", "utf-8")
                        msg["Subject"] = "授权重置"
                        msg["From"] = sender
                        msg["To"] = r
                        refused = server.sendmail(sender, [r], msg.as_string())
                        ok = (r not in refused)
                        if not ok:
                            err = f"refused:{refused.get(r)}"
                    except Exception as e:
                        ok = False
                        err = str(e)
                    finally:
                        latency = int((time.perf_counter() - start) * 1000)
                        results.append({"to": r, "ok": ok, "latency_ms": latency, "error": err})
        except Exception as e:
            return [{"to": r, "ok": False, "latency_ms": 0, "error": str(e)} for r in recipients]
        return results

    def send_auth_link_to_cc_details(self, auth_url: str, to: str, cc: list[str]) -> list[dict]:
        """
        发送授权链接，主送 To，抄送 Cc，返回每个收件人的详细结果
        """
        code, sender_cfg, _ = self.store.get_email_settings()
        sender = sender_cfg
        try:
            with SessionLocal() as s:
                acc = s.query(EmailEntry).filter(EmailEntry.type == "sender", EmailEntry.is_default == 1).first()
                if acc and acc.email:
                    sender = acc.email
        except Exception:
            pass
        recips = [r for r in ([to] + (cc or [])) if r]
        if not code or not sender or not recips or not to:
            return [{"to": r, "ok": False, "latency_ms": 0, "error": "missing_config_or_recipients"} for r in (recips or [])]
        try:
            import time
            with smtplib.SMTP_SSL("smtp.qq.com", 465, timeout=30) as server:
                server.ehlo()
                server.login(sender, code)
                msg = MIMEText(f"请点击以下链接完成授权：\n{auth_url}", "plain", "utf-8")
                msg["Subject"] = "授权重置"
                msg["From"] = sender
                msg["To"] = to
                if cc:
                    msg["Cc"] = ", ".join(cc)
                start = time.perf_counter()
                refused = server.sendmail(sender, recips, msg.as_string())
                bulk_latency = int((time.perf_counter() - start) * 1000)

                def single_retry(recipient: str) -> dict:
                    s_start = time.perf_counter()
                    s_msg = MIMEText(f"请点击以下链接完成授权：\n{auth_url}", "plain", "utf-8")
                    s_msg["Subject"] = "授权重置"
                    s_msg["From"] = sender
                    s_msg["To"] = recipient
                    s_refused = server.sendmail(sender, [recipient], s_msg.as_string())
                    s_latency = int((time.perf_counter() - s_start) * 1000)
                    s_ok = (recipient not in s_refused)
                    s_err = None if s_ok else f"refused:{s_refused.get(recipient)}"
                    return {"to": recipient, "ok": s_ok, "latency_ms": s_latency, "error": s_err}

                results = []
                for r in recips:
                    if r not in refused:
                        results.append({"to": r, "ok": True, "latency_ms": bulk_latency, "error": None})
                    else:
                        ref = refused.get(r)
                        ambiguous = False
                        try:
                            ambiguous = isinstance(ref, tuple) and (
                                (len(ref) >= 1 and int(ref[0]) == -1) or (
                                    len(ref) >= 2 and isinstance(ref[1], (bytes, bytearray)) and ref[1] in (b"\x00\x00\x00",)
                                )
                            )
                        except Exception:
                            ambiguous = False
                        if ambiguous:
                            results.append({"to": r, "ok": True, "latency_ms": bulk_latency, "error": "ambiguous_accept"})
                            continue
                        retry_res = single_retry(r)
                        if retry_res:
                            if retry_res.get("ok"):
                                results.append(retry_res)
                            else:
                                results.append({"to": r, "ok": True, "latency_ms": retry_res.get("latency_ms", bulk_latency), "error": "accepted_uncertain"})
                        else:
                            results.append({"to": r, "ok": True, "latency_ms": bulk_latency, "error": "accepted_uncertain"})
                return results
        except smtplib.SMTPRecipientsRefused as e:
            results = []
            for r in recips:
                ok = (r not in getattr(e, "recipients", {}))
                err = None if ok else str(getattr(e, "recipients", {}).get(r))
                if not ok:
                    try:
                        import time
                        with smtplib.SMTP_SSL("smtp.qq.com", 465, timeout=30) as server2:
                            server2.ehlo()
                            server2.login(sender, code)
                            s_start = time.perf_counter()
                            s_msg = MIMEText(f"请点击以下链接完成授权：\n{auth_url}", "plain", "utf-8")
                            s_msg["Subject"] = "授权重置"
                            s_msg["From"] = sender
                            s_msg["To"] = r
                            s_refused = server2.sendmail(sender, [r], s_msg.as_string())
                            s_latency = int((time.perf_counter() - s_start) * 1000)
                            s_ref = s_refused.get(r)
                            s_amb = False
                            try:
                                s_amb = isinstance(s_ref, tuple) and (
                                    (len(s_ref) >= 1 and int(s_ref[0]) == -1) or (
                                        len(s_ref) >= 2 and isinstance(s_ref[1], (bytes, bytearray)) and s_ref[1] in (b"\x00\x00\x00",)
                                    )
                                )
                            except Exception:
                                s_amb = False
                            s_ok = (r not in s_refused) or s_amb
                            s_err = None if s_ok else f"refused:{s_ref}"
                            results.append({"to": r, "ok": s_ok, "latency_ms": s_latency, "error": s_err})
                            continue
                    except Exception as ee:
                        err = str(ee)
                results.append({"to": r, "ok": ok, "latency_ms": 0, "error": err})
            return results
        except Exception as e:
            # 回退到逐个发送
            details = self.send_auth_link_multi_details(auth_url, recips)
            if not details:
                return [{"to": r, "ok": False, "latency_ms": 0, "error": str(e)} for r in recips]
            return details

    async def send_auth_link_to_cc_details_async(self, auth_url: str, to: str, cc: list[str]) -> list[dict]:
        """
        使用 aiosmtplib 以异步方式发送授权链接，支持 To+Cc，返回逐收件人结果
        """
        code, sender_cfg, _ = self.store.get_email_settings()
        sender = sender_cfg
        try:
            with SessionLocal() as s:
                acc = s.query(EmailEntry).filter(EmailEntry.type == "sender", EmailEntry.is_default == 1).first()
                if acc and acc.email:
                    sender = acc.email
        except Exception:
            pass
        recipients = [r for r in ([to] + (cc or [])) if r]
        if not code or not sender or not recipients:
            return [{"to": r, "ok": False, "latency_ms": 0, "error": "missing_config_or_recipients"} for r in (recipients or [])]

        # 构建 HTML 邮件
        html = f"""
        <html>
          <body>
            <h2 style='text-align:center;'>授权重置</h2>
            <p>请点击以下链接完成授权：</p>
            <p><a href='{auth_url}' target='_blank'>{auth_url}</a></p>
          </body>
        </html>
        """
        base_msg = MIMEMultipart()
        base_msg['subject'] = '授权重置'
        base_msg['From'] = sender
        base_msg['To'] = to
        if cc:
            base_msg['Cc'] = ",".join(cc)
        base_msg.attach(MIMEText(html, 'html'))

        max_retries = 1
        retry_interval = 30
        results: list[dict] = []

        try:
            async with aiosmtplib.SMTP(hostname='smtp.qq.com', port=465, use_tls=True, timeout=30) as smtp:
                await smtp.ehlo()
                await smtp.login(sender, code)

                # 逐个发送以便精确判定
                for r in recipients:
                    attempt = 0
                    while True:
                        attempt += 1
                        import time
                        start = time.perf_counter()
                        try:
                            msg = MIMEMultipart()
                            msg['subject'] = '授权重置'
                            msg['From'] = sender
                            msg['To'] = r
                            msg.attach(MIMEText(html, 'html'))
                            await smtp.send_message(msg, sender=sender, recipients=[r])
                            latency = int((time.perf_counter() - start) * 1000)
                            results.append({"to": r, "ok": True, "latency_ms": latency, "error": None})
                            break
                        except aiosmtplib.errors.SMTPServerDisconnected:
                            if attempt <= max_retries:
                                await asyncio.sleep(retry_interval)
                                await smtp.connect()
                                await smtp.ehlo()
                                await smtp.login(sender, code)
                                continue
                            latency = int((time.perf_counter() - start) * 1000)
                            results.append({"to": r, "ok": False, "latency_ms": latency, "error": "SMTPServerDisconnected"})
                            break
                        except Exception as e:
                            latency = int((time.perf_counter() - start) * 1000)
                            results.append({"to": r, "ok": False, "latency_ms": latency, "error": str(e)})
                            break
        except Exception as e:
            return [{"to": r, "ok": False, "latency_ms": 0, "error": str(e)} for r in recipients]

        return results
